#define   __LIBRARY__
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/sem.h>


#define M 530    		/*打出数字总数*/
#define N 5       		/*消费者进程数*/
#define BUFSIZE 10      /*缓冲区大小*/


_syscall2(sem_t*,sem_open,const char *,name,unsigned int,value);
_syscall1(int,sem_wait,sem_t*,sem);
_syscall1(int,sem_post,sem_t*,sem);
_syscall1(int,sem_unlink,const char *,name);


int main()
{
    
	sem_t *empty, *full, *mutex;/*3个信号量*/
	int fd; /*共享缓冲区文件描述符*/
    int  i,j,k,child;
    int  data;/*写入的数据*/
    pid_t pid;
    int  buf_out = 0;  /*从缓冲区读取位置*/
    int  buf_in = 0;   /*写入缓冲区位置*/
	/*打开信号量*/

  
	 if((mutex = sem_open("mutex",1)) == NULL)
    {
        perror("sem_open() error!\n");
        return -1;
    }
    if((empty = sem_open("empty",10)) == NULL)
    {
        perror("sem_open() error!\n");
        return -1;
    }
    if((full = sem_open("full",0)) == NULL)
    {
        perror("sem_open() error!\n");
        return -1;
    }

    fd = open("buffer.txt", O_CREAT|O_TRUNC|O_RDWR,0666); 
    lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);/*刷新了40个字节的缓冲区，存放10个数字*/
    write(fd,(char *)&buf_out,sizeof(int));/*将待读取位置存入buffer后,以便子进程之间通信*/
    /*生产者进程*/
    if((pid=fork())==0)
    {
		printf("I'm producer. pid = %d\n", getpid());
		/*生产多少个产品就循环几次*/
        for( i = 0 ; i < M; i++)
        {
			/*empty大于0,才能生产*/
            sem_wait(empty);
            sem_wait(mutex);
            /*写入一个字符*/
            lseek(fd, buf_in*sizeof(int), SEEK_SET); 
            write(fd,(char *)&i,sizeof(int)); 
            /*更新写入缓冲区位置，保证在0-9之间*/
			/*生产完一轮产品（文件缓冲区只能容纳BUFSIZE个产品编号）后*/
			/*将缓冲文件的位置指针重新定位到文件首部。*/			
            buf_in = (buf_in + 1) % BUFSIZE;
            sem_post(mutex);
            sem_post(full);/*共享区中已使用资源++，唤醒消费者线程*/
        }
        printf("producer end.\n");
        fflush(stdout);/*确保将输出立刻输出到标准输出。*/
        return 0;
    }
	else if(pid < 0)
    {
        perror("Fail to fork!\n");
        return -1;
    }
	/*消费者进程*/
    for( j = 0; j < N ; j++ )
    {
        if((pid=fork())==0)
        {
			for( k = 0; k < M/N; k++ )
            {
                sem_wait(full);/*共享区中已使用资源--,一开始为0会阻塞此处*/
                sem_wait(mutex);
				
                /*获得读取位置*/
                lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);
                read(fd,(char *)&buf_out,sizeof(int));
                /*读取数据*/
                lseek(fd,buf_out*sizeof(int),SEEK_SET);
                read(fd,(char *)&data,sizeof(int));
                /*写入读取位置*/
                buf_out = (buf_out + 1) % BUFSIZE;
                lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);
                write(fd,(char *)&buf_out,sizeof(int));

                sem_post(mutex);
                sem_post(empty);/*共享区中剩余资源++，唤醒生产者进程*/
                /*消费资源*/
                printf("%d:  %d\n",getpid(),data);
                fflush(stdout);
            }
			printf("child-%d: pid = %d end.\n", j, getpid());
            return 0;
        }
		else if(pid<0)
		{
			perror("Fail to fork!\n");
			return -1;
		}
	}
	/*回收线程资源*/
    child = N + 1;
    while(child--)
        wait(NULL);
    /*释放信号量*/
    sem_unlink("full");
    sem_unlink("empty");
    sem_unlink("mutex");
    /*释放资源*/
    close(fd);
    return 0;
}
